package com.vast.metrix.recorder.bustraffic.controllers;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.internal.ViewSite;

import com.vast.ui.component.protocol.Protocol;
import com.vast.ui.component.socket.ISocketServer;
import com.vast.ui.component.socket.ISocketService;
import com.vast.ui.component.socket.ServerServiceController;
import com.vast.ui.component.socket.listener.IReceiveListener;
import com.vast.ui.component.socket.listener.IRefreshListener;
import com.vast.ui.component.socket.listener.ISocketConnectionListener;
import com.vast.ui.component.socket.models.Request;
import com.vast.ui.component.socket.models.RequestQueue;
import com.vast.ui.component.socket.state.Sequence;
import com.vast.ui.component.toolbar.ToolBarAction;
import com.vast.ui.component.util.ConsoleLoggerSingleton;
import com.vast.ui.component.util.Logger;
import com.vast.ui.component.util.SaveDialog;
import com.vast.metrix.recorder.bustraffic.Activator;
import com.vast.metrix.recorder.bustraffic.models.BusTraffic;
import com.vast.metrix.recorder.bustraffic.models.BusTrafficCollection;
import com.vast.metrix.recorder.bustraffic.preferences.PreferenceConstants;
import com.vast.metrix.recorder.bustraffic.viewers.BusTrafficViewer;

public class InterfaceController  extends ToolBarAction implements ISocketConnectionListener, IReceiveListener, IRefreshListener {
	private final Display display;
	private Composite parent;
	private final ISocketServer server;
	private final RequestQueue sendQueue;
	private boolean isReadyForReset;
	private boolean isRecording = false;
	private final BusTrafficViewer viewer;
	private final Sequence          sequence;
	private AutoRefreshFire         fireController = null;
	private int samplingRate_ms = 10;
	private int                     xAxisLengthMilliseconds	= 500;
	private BusTrafficCollection[]  busTrafficCollectionArray;
	private SaveDialog saveDialog;
	public  final Logger     logger;
	
	public InterfaceController(IViewSite view, Composite parent, int serverPort) {
		super(view);
		this.parent                  = parent;
		this.display                 = parent.getDisplay();
		this.logger                  = ConsoleLoggerSingleton.getInstance();
		this.sendQueue               = new RequestQueue();
		this.sequence                = new Sequence();
		this.viewer                  = new BusTrafficViewer(parent, "BusTraffic Recorder");
		this.server                  = new ServerServiceController(Activator.PLUGIN_ID, serverPort, sequence, sendQueue, Activator.getDefault().getPreferenceStore().getInt(PreferenceConstants.P_WAIT_VALUE), true, false);
		this.server.addSocketConnectionListener(this);
		this.server.addReceiveListener(this);
		this.isReadyForReset = false;
		
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root  = workspace.getRoot();
		String initialSavePath = root.getLocation().toOSString();
		String[] filterNames = new String [] {"CSV Files", "All Files (*)"};
		String[] filterExtensions = new String [] {"*.csv;*.txt;*.", "*"};
		this.saveDialog = new SaveDialog(parent, initialSavePath, filterNames, filterExtensions);
		
	}
	
	public void addReceiveListener(IReceiveListener listener){
		server.addReceiveListener(listener);
	}
	
	public void removeReceiveListener(IReceiveListener listener) {
		server.removeReceiveListener(listener);
	}

	public void addAction() {
		// TODO Auto-generated method stub
		
	}

	public void deleteAction() {
		// TODO Auto-generated method stub
		
	}

	public void startAction() {
		// TODO Auto-generated method stub
		this.server.openConnection();
		if(this.isReadyForReset) {
			//sliderPanel.reset();
			this.isReadyForReset = false;
		}
		//
		setStopActionEnable();
		saveFileAction.setEnabled(false);
	}

	public void stopAction() {
		// TODO Auto-generated method stub
		this.isReadyForReset = true;
		this.server.closeConnection();
		setStartActionEnable();
		setStartChartActionEnable();
		pauseRecordAction();
	}

	public void notifyStateChange(Sequence sequence) {
		// TODO Auto-generated method stub
		if(sequence.isReceiveConnection()|| sequence.isSendConnection()){
			stopAction();
		} else {
			setStopActionEnable();	
		}
	}

	public void startChartAction() {
		// TODO Auto-generated method stub
		if (this.fireController != null)
			this.fireController.setUpdateChart(true);
		setPauseChartActionEnable();
	}
	
	public void pauseChartAction() {
		// TODO Auto-generated method stub
		if (this.fireController != null)
			this.fireController.setUpdateChart(false);
		setStartChartActionEnable();
	}

	public void startRecordAction() {
		// TODO Auto-generated method stub
		setPauseRecordActionEnable();
		this.isRecording = true;
		saveFileAction.setEnabled(false);
		if (busTrafficCollectionArray != null) {
			for (int i = 0; i < busTrafficCollectionArray.length; i++) {
				busTrafficCollectionArray[i].setRecording(true);
			}
		}
	}
	
	public void pauseRecordAction() {
		// TODO Auto-generated method stub
		setStartRecordActionEnable();
		if(this.isRecording) {
			saveFileAction.setEnabled(true);
		}
		if (busTrafficCollectionArray != null) {
			for (int i = 0; i < busTrafficCollectionArray.length; i++) {
				busTrafficCollectionArray[i].setRecording(false);
			}
		}
	}
	
	public void received(Request request) {
		// TODO Auto-generated method stub
		switch (request.command) {
			case Protocol.PK_BUSTRAFFIC_INITIALIZE: {
				this.samplingRate_ms = request.readInt();
				byte[] tempData = new byte[request.data.length -8];
				System.arraycopy(request.data, 4, tempData, 0, tempData.length);
				String header = new String(tempData);
				String[] busNames = header.split(";");
				this.busTrafficCollectionArray    = new BusTrafficCollection[busNames.length];
				for (int i = 0; i < busNames.length; i++ ) {
					this.busTrafficCollectionArray[i] = new BusTrafficCollection(busNames[i], this.isRecording);
				}
				viewer.createChart(busNames, this.samplingRate_ms, xAxisLengthMilliseconds);
				this.fireController = new AutoRefreshFire(this.sequence, this.display, this.viewer.getRefreshTrigger(), Activator.getDefault().getPreferenceStore().getInt(PreferenceConstants.P_REFRESH_RATE_MS));
				this.fireController.addListener(this);
				Executor ex = Executors.newSingleThreadExecutor();
				ex.execute(this.fireController);
				System.out.print(header);
				break;
			}
			
			case Protocol.PK_BUSTRAFFIC_UPDATE: {
				int busIndex = request.readInt();
				long   currentTime_ms  =  (long)request.readInt();
				double activity       = (double)request.readInt();
				double latency        = (double)request.readInt();
				double utilization    = (double)request.readInt();
				double transferLength = (double)request.readInt();
				viewer.addBusTraffic(busIndex, currentTime_ms, transferLength, activity, latency, utilization);
				this.busTrafficCollectionArray[busIndex].add(new BusTraffic(currentTime_ms, activity, latency, utilization, transferLength));
				break;
			}
		}
	}

	public void refresh() {
		// TODO Auto-generated method stub
		this.display.asyncExec(new Runnable() {
			public void run() {
				for (int i = 0; i < busTrafficCollectionArray.length; i++) {
					BusTraffic maximumValue = busTrafficCollectionArray[i].getMaximumValue();
					viewer.updateUpperValueOfRangeAxis(i, maximumValue, false);
				}
			}
		});
		
	}

	public void saveFile() throws IOException {
		final String fileName = this.saveDialog.openSaveDialog("metrix_bus_filter", true);
		if(fileName != null) {
			final int numberOfBusTrafficCollectionArray = this.busTrafficCollectionArray.length;
			if(numberOfBusTrafficCollectionArray == 0){
				return;
			}
					    
			File file = new File(fileName);
			final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
			/* Bus Name */
			String header = "info(SamplingRate_ms=" + samplingRate_ms + ")";
			header += ":BusName=" + this.busTrafficCollectionArray[0].getBusName();
			for (int i = 1; i < this.busTrafficCollectionArray.length; i++) {
				header = header + ";" +  this.busTrafficCollectionArray[i].getBusName(); 
			}
			header += "\n";
			bufferedWriter.write(header);
			
			String comment = "#Time[ms]";
			String commentBody = ",TransferLength [byte],Activity [times], Latency [ticks], Utilization [ticks]";
			comment += commentBody;
			for (int i = 1; i < numberOfBusTrafficCollectionArray; i++) {
				comment += commentBody;
			}
			comment += "\n";
			bufferedWriter.write(comment);
			
			/*
			for (int i = 0; i < this.busTrafficCollectionArray[0].getSizeOfbusTraffic(); i++) {
				String line = this.busTrafficCollectionArray[0].getBusTrafficAsString(i, true);
				for (int j = 1; j < this.busTrafficCollectionArray.length; j++ ) {
					line += "," + this.busTrafficCollectionArray[j].getBusTrafficAsString(i, false);
				}
				line += "\n";
				bufferedWriter.write(line);
			}
			bufferedWriter.flush();
			bufferedWriter.close();
			*/
			ProgressMonitorDialog dialog = new ProgressMonitorDialog(this.parent.getShell());
			try {
				dialog.run(true, true, new IRunnableWithProgress() {
					public void run(IProgressMonitor monitor)
						throws InvocationTargetException, InterruptedException {
							int numberOfItem = busTrafficCollectionArray[numberOfBusTrafficCollectionArray - 1].getSizeOfbusTraffic();
							monitor.beginTask("Saving captured data to " + fileName, numberOfItem);
							// Monitor process
							for (int i = 0; i < numberOfItem; i++) {
								String line = busTrafficCollectionArray[0].getBusTrafficAsString(i, true);
								for (int j = 1; j < busTrafficCollectionArray.length; j++ ) {
									line += "," + busTrafficCollectionArray[j].getBusTrafficAsString(i, false);
								}
								line += "\n";
								try {
									bufferedWriter.write(line);
									bufferedWriter.flush();
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
								monitor.subTask((i + 1) + "/" + busTrafficCollectionArray[0].getSizeOfbusTraffic() + " lines written.");
								Thread.sleep(5);
								monitor.worked(1);
							}
							try {
								bufferedWriter.close();
								monitor.done();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							
						}
					} );
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}
	
	
	public void clearDataset() {
		for(int i = 0; i < this.busTrafficCollectionArray.length; i++) {
			this.busTrafficCollectionArray[i].clear();
		}
	}
	
	public void clearDataset(int start, int end) {
		for(int i = 0; i < this.busTrafficCollectionArray.length; i++) {
			this.busTrafficCollectionArray[i].clear(start, end);
		}
	}
	
	@Override
	public void saveAction() {
		// TODO Auto-generated method stub
		setStartRecordActionEnable();
		saveFileAction.setEnabled(false);
		try {
			saveFile();
			clearDataset();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.isRecording = false;
		//startRecordAction.setEnabled(true);
	}
	
	public void clearViewer() {
		
	}
}
